/*
 * Copyright (c)  2020,  DataPipeline
 * All rights reserved.
 *
 * Id:AbstractDialect.java   2020-07-18 15:15 wanglijun
 */
package cn.datacare.orm.dialect;

import cn.datacare.orm.annotation.DateTemplate;
import cn.datacare.orm.annotation.TableTemplate;
import cn.datacare.orm.annotation.builder.AttributeBuilderHolder;
import cn.datacare.orm.annotation.builder.AttributePersistBuilder;
import cn.datacare.orm.annotation.id.Id;
import cn.datacare.orm.annotation.id.IdType;
import cn.datacare.orm.constant.SqlSourceType;
import cn.datacare.orm.conversion.NameConversion;
import cn.datacare.orm.database.ClassDesc;
import cn.datacare.orm.database.TableDesc;
import cn.datacare.orm.engine.Beetl;
import cn.datacare.orm.exception.OrmCoreException;
import cn.datacare.orm.handler.DefaultKeyWordHandler;
import cn.datacare.orm.handler.KeyWordHandler;
import cn.datacare.orm.manager.impl.MetaDataManager;
import cn.datacare.orm.source.SQLSource;
import cn.datacare.orm.source.SQLTableSource;
import cn.datacare.orm.util.Separator;
import cn.datacare.orm.util.kit.BeanKit;
import org.beetl.core.Configuration;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;

import static cn.datacare.orm.engine.EngineConfig.OFFSET_START_ZERO;
import static cn.datacare.orm.exception.OrmErrorCode.ID_EXPECTED_ONE_ERROR;
import static cn.datacare.orm.exception.OrmErrorCode.ID_NOT_FOUND;
import static cn.datacare.orm.util.Separator.*;
import static cn.datacare.orm.util.StringUtils.EMPTY;

/**
 * <p>
 * Title: 数据库方言
 * </p>
 * <p>
 * Description: 数据库方言
 * </p>
 * <p>
 * Copyright: Copyright (c) 2020
 * </p>
 * <p>
 * Company/Department: DataPipeline
 * </p>
 *
 * @author wanglijun
 * @version 1.0
 * @since 2020-07-18 15:15
 */
public abstract class AbstractDialect implements Dialect {

    /***
     * 定界符开始符号
     */
    protected String statementStart;
    /***
     * 定界符结束符号
     */
    protected String statementEnd;
    /***
     * 站位符开始符号
     */
    protected String holderStart;
    /**
     * 站位符结束符号
     */
    protected String holderEnd;

    /**
     * NameConversion
     */
    private NameConversion nameConversion;
    /**
     * MetaDataManager
     */
    private MetaDataManager metadataManager;
    /***
     * 分行
     */
    private final String lineSeparator = LINE_BREAK;

    private KeyWordHandler keyWordHandler = new DefaultKeyWordHandler();
    //翻页从0还是1开始，默认从1开始
    private boolean offsetStartZero = false;


    public AbstractDialect() {
    }


    @Override
    public void init(Beetl beetl) {
        Configuration cf = beetl.getGroupTemplate().getConf();
        this.statementStart = cf.getStatementStart();
        this.statementEnd = cf.getStatementEnd();
        if (this.statementEnd == null || this.statementEnd.length() == 0) {
            this.statementEnd = this.lineSeparator;
        }
        this.holderStart = cf.getPlaceholderStart();
        this.holderStart = cf.getPlaceholderEnd();
        this.offsetStartZero = Boolean.parseBoolean(beetl.getProperties().getProperty(OFFSET_START_ZERO).trim());
    }

    /**
     * 设置命名转换器
     *
     * @param nameConversion NC
     */
    @Override
    public void setNameConversion(NameConversion nameConversion) {
        this.nameConversion = nameConversion;
    }


    /**
     * 获取 MetaDataManager
     *
     * @return MetaDataManager MetaDataManager
     */
    @Override
    public MetaDataManager getMetaDataManager() {
        return this.metadataManager;
    }

    /**
     * 设置 MetaDataManager
     *
     * @param metaDataManager MetaDataManager
     */
    @Override
    public void setMetaDataManager(MetaDataManager metaDataManager) {
        this.metadataManager = metaDataManager;
    }

    /**
     * 获取 KeyWordHandler
     *
     * @return KeyWordHandler
     */
    @Override
    public KeyWordHandler getKeyWordHandler() {
        return this.keyWordHandler;
    }

    /**
     * 设置KeyWordHandler
     *
     * @param keyWordHandler
     */
    @Override
    public void setKeyWordHandler(KeyWordHandler keyWordHandler) {
        this.keyWordHandler = keyWordHandler;
    }

    /***
     * 默认不支持序列
     * @param sequenceName SequenceName
     * @return String 序列名称
     */
    @Override
    public String getSequenceValue(String sequenceName) {
        throw new UnsupportedOperationException("不支持序列");
    }

    /**
     * 获取StatementStart
     *
     * @return String StatementStart
     */
    public String getStatementStart() {
        return this.statementStart;
    }

    /**
     * 获取 statementEnd
     *
     * @return String statementEnd
     */
    public String getStatementEnd() {
        return this.statementEnd;
    }

    /**
     * HolderStart
     *
     * @return HolderStart
     */
    public String getHolderStart() {
        return this.holderStart;
    }

    /**
     * 获取HolderEnd
     *
     * @return holderEnd
     */
    public String getHolderEnd() {
        return this.holderEnd;
    }


    @Override
    public SQLSource genSelectById(Class<?> cls) {
        String tableName = nameConversion.getTableName(cls);
        TableDesc table = this.metadataManager.getTable(tableName);
        String condition = appendIdCondition(cls);
        return new SQLTableSource(new StringBuilder("select * from ").append(getTableName(table)).append(condition).toString());
    }

    @Override
    public SQLSource genExistSql(Class<?> cls) {
        String tableName = nameConversion.getTableName(cls);
        TableDesc table = this.metadataManager.getTable(tableName);
        String condition = appendIdCondition(cls);
        return new SQLTableSource(new StringBuilder("select count(1) from ").append(getTableName(table)).append(condition).toString());

    }

    @Override
    public SQLSource genSelectByIdForUpdate(Class<?> cls) {
        SQLSource source = genSelectById(cls);
        String template = source.getTemplate();
        template = template + " for update";
        source.setTemplate(template);
        return source;
    }

    @Override
    public SQLSource genSelectByTemplate(Class<?> cls) {
        String tableName = nameConversion.getTableName(cls);
        TableDesc table = this.metadataManager.getTable(tableName);
        String condition = getSelectTemplate(cls);
        String appendSql = EMPTY;
        TableTemplate t = (TableTemplate) BeanKit.getAnnotation(cls, TableTemplate.class);

        if (t != null) {
            appendSql = t.value();
            if ((appendSql == null || appendSql.length() == 0) && table.getIdNames().size() != 0) {

                appendSql = " order by ";
                Set<String> ids = table.getIdNames();
                int i = 0;
                for (String id : ids) {
                    appendSql += id + " desc";
                    if (i == (ids.size() - 1)) {
                        break;
                    }
                    appendSql += " , ";
                }

            }
        }
        String sql = new StringBuilder("select * from ").append(getTableName(table)).append(condition).append(appendSql).toString();
        return new SQLTableSource(sql);
    }

    @Override
    public SQLSource genSelectCountByTemplate(Class<?> cls) {
        String tableName = nameConversion.getTableName(cls);
        TableDesc table = this.metadataManager.getTable(tableName);
        String condition = getSelectTemplate(cls);

        return new SQLTableSource(new StringBuilder("select count(1) from ").append(getTableName(table)).append(condition).toString());

    }

    protected String getSelectTemplate(Class<?> cls) {
        StringBuilder condition = new StringBuilder(" where 1=1 " + lineSeparator);
        String tableName = nameConversion.getTableName(cls);
        TableDesc table = this.metadataManager.getTable(tableName);
        ClassDesc classDesc = table.getClassDesc(cls, nameConversion);
        Iterator<String> cols = classDesc.getInCols().iterator();
        Iterator<String> attrs = classDesc.getAttrs().iterator();

        while (cols.hasNext() && attrs.hasNext()) {
            String col = cols.next();
            String attr = attrs.next();
            if (classDesc.isDateType(attr)) {
                DateTemplate dateTemplate = BeanKit.getAnnotation(classDesc.getTargetClass(), attr, DateTemplate.class);
                String sql;
                if (dateTemplate == null) {
                    sql = this.appendWhere(cls, table, col, attr);
                } else {
                    sql = this.genDateAnnotationSql(dateTemplate, cls, col);
                }
                condition.append(sql);
            } else {
                condition.append(appendWhere(cls, table, col, attr));
            }

//			condition = condition + appendWhere(cls,table, col);

        }
        return condition.toString();
    }

    @Override
    public SQLSource genDeleteById(Class<?> cls) {
        String tableName = nameConversion.getTableName(cls);
        TableDesc table = this.metadataManager.getTable(tableName);
        ClassDesc classDesc = table.getClassDesc(cls, this.nameConversion);
        String condition = appendIdCondition(cls);
        if (classDesc.getLogicDeleteAttrName() == null) {
            return new SQLTableSource(new StringBuilder("delete from ").append(getTableName(table)).append(condition).toString());
        } else {
            String col = this.nameConversion.getColName(cls, classDesc.getLogicDeleteAttrName());
            return new SQLTableSource(new StringBuilder("update  ")
                    .append(getTableName(table)).append(" set ")
                    .append(col).append(" = ").append(classDesc.getLogicDeleteAttrValue()).append(condition).toString());
        }


    }

    @Override
    public SQLSource genSelectAll(Class<?> cls) {
        String tableName = nameConversion.getTableName(cls);
        TableDesc table = this.metadataManager.getTable(tableName);
        tableName = table.getName();
        return new SQLTableSource(new StringBuilder("select * from ").append(getTableName(table)).toString());
    }

    @Override
    public SQLSource genUpdateById(Class<?> cls) {
        String tableName = nameConversion.getTableName(cls);
        TableDesc table = this.metadataManager.getTable(tableName);
        ClassDesc classDesc = table.getClassDesc(cls, nameConversion);
        StringBuilder sql = new StringBuilder("update ").append(getTableName(table)).append(" set ").append(lineSeparator);
        Iterator<String> cols = classDesc.getInCols().iterator();
        Iterator<String> properties = classDesc.getAttrs().iterator();

        List<String> idCols = classDesc.getIdCols();
        while (cols.hasNext() && properties.hasNext()) {
            String col = cols.next();
            String prop = properties.next();

            if (classDesc.isUpdateIgnore(prop)) {
                continue;
            }
            if (idCols.contains(col)) {
                //主键不更新
                continue;
            }

            if (prop.equals(classDesc.getVersionProperty())) {
                //版本字段
                sql.append(this.getKeyWordHandler().getCol(col)).append("=")
                        .append(this.getKeyWordHandler().getCol(col)).append("+1").append(",");
                continue;
            }

            //普通情况
            sql.append(appendSetColumnAbsolute(cls, table, col, prop));
        }

        String condition = appendIdCondition(cls);
        condition = appendVersion(condition, classDesc);
        sql = removeComma(sql, condition);
        return new SQLTableSource(sql.toString());
    }

    @Override
    public SQLSource genUpdateAbsolute(Class<?> cls) {
        String tableName = nameConversion.getTableName(cls);
        TableDesc table = this.metadataManager.getTable(tableName);
        ClassDesc classDesc = table.getClassDesc(cls, nameConversion);
        StringBuilder sql = new StringBuilder("update ").append(getTableName(table)).append(" set ").append(lineSeparator);
        Iterator<String> cols = classDesc.getInCols().iterator();
        Iterator<String> properties = classDesc.getAttrs().iterator();

        List<String> idCols = classDesc.getIdCols();
        while (cols.hasNext() && properties.hasNext()) {
            String col = cols.next();
            String prop = properties.next();

            if (classDesc.isUpdateIgnore(prop)) {
                continue;
            }
            if (idCols.contains(col)) {
                //主键不更新
                continue;
            }
            if (prop.equals(classDesc.getVersionProperty())) {
                //版本字段
                sql.append(this.getKeyWordHandler().getCol(col)).append("=")
                        .append(this.getKeyWordHandler().getCol(col)).append("+1").append(",");
                continue;
            }

            sql.append(appendSetColumnAbsolute(cls, table, col, prop));
        }

        sql = removeComma(sql, EMPTY);
        return new SQLTableSource(sql.toString());
    }

    private String appendVersion(String condition, ClassDesc desc) {
        String property = desc.getVersionProperty();
        if (property == null) {
            return condition;
        }
        String col = this.nameConversion.getColName(desc.getTargetClass(), property);
        condition = condition + " and " + this.getKeyWordHandler().getCol(col) + " = "
                + this.holderStart + property + this.holderEnd;
        return condition;
    }

    @Override
    public SQLSource genUpdateTemplate(Class<?> cls) {
        String tableName = nameConversion.getTableName(cls);
        TableDesc table = this.metadataManager.getTable(tableName);
        ClassDesc classDesc = table.getClassDesc(cls, nameConversion);
        StringBuilder sql = new StringBuilder("update ").append(getTableName(table)).append(" set ").append(lineSeparator);
        String condition = appendIdCondition(cls);
        condition = appendVersion(condition, classDesc);
        Iterator<String> cols = classDesc.getInCols().iterator();
        Iterator<String> properties = classDesc.getAttrs().iterator();

        List<String> idCols = classDesc.getIdCols();
        while (cols.hasNext() && properties.hasNext()) {
            String col = cols.next();
            String prop = properties.next();
            if (classDesc.isUpdateIgnore(prop)) {
                continue;
            }
            if (idCols.contains(col)) {
                continue;
            }
            if (prop.equals(classDesc.getVersionProperty())) {
                //版本字段
                sql.append(this.getKeyWordHandler().getCol(col)).append("=")
                        .append(this.getKeyWordHandler().getCol(col)).append("+1").append(",");
                continue;
            }
            sql.append(appendSetColumn(cls, table, col, prop));
        }
        StringBuilder trimSql = new StringBuilder();

        trimSql.append(this.getStatementStart()).append("trim(){\n").append(this.getStatementEnd()).append("\n").append(sql);
        trimSql.append(this.getStatementStart()).append("}\n").append(this.getStatementEnd());
        sql = removeComma(trimSql, condition);
        if (condition == null) {
            throw new OrmCoreException(ID_EXPECTED_ONE_ERROR);
        }
        return new SQLTableSource(sql.toString());

    }

    @Override
    public SQLSource genUpdateAll(Class<?> cls) {
        String tableName = nameConversion.getTableName(cls);
        TableDesc table = this.metadataManager.getTable(tableName);
        ClassDesc classDesc = table.getClassDesc(cls, nameConversion);
        StringBuilder sql = new StringBuilder("update ").append(getTableName(table)).append(" set ").append(lineSeparator);
        Iterator<String> cols = classDesc.getInCols().iterator();
        Iterator<String> properties = classDesc.getAttrs().iterator();

        sql.append(this.lineSeparator).append(this.getStatementStart());
        sql.append("trim({suffixOverrides:','}){").append(this.lineSeparator);

        List<String> idCols = classDesc.getIdCols();
        while (cols.hasNext() && properties.hasNext()) {
            String col = cols.next();
            String prop = properties.next();
            if (classDesc.isUpdateIgnore(prop)) {
                continue;
            }
            if (idCols.contains(col)) {
                //主键不更新
                continue;
            }
            sql.append(appendSetColumn(cls, table, col, prop));
        }

        sql.append(this.lineSeparator).append(this.getStatementStart());
        sql.append("}").append(this.lineSeparator).append(this.getStatementEnd());

        return new SQLTableSource(sql.toString());
    }

    @Override
    public SQLSource genInsert(Class<?> cls) {
        return generalInsert(cls, false);
    }


    @Override
    public SQLSource genInsertTemplate(Class<?> cls) {
        return generalInsert(cls, true);
    }


    protected SQLSource generalInsert(Class<?> cls, boolean template) {
        String tableName = nameConversion.getTableName(cls);
        TableDesc table = this.metadataManager.getTable(tableName);
        ClassDesc classDesc = table.getClassDesc(cls, nameConversion);
        StringBuilder sql = new StringBuilder(getInsertBaseSql(classDesc, table) + lineSeparator);
        StringBuilder colSql = new StringBuilder("(");
        StringBuilder valSql = new StringBuilder(" VALUES (");
        if (template) {
            //动态拼，需要使用trim去掉最后可能的空格
            colSql.append(this.lineSeparator).append(this.getStatementStart());
            colSql.append("trim({suffixOverrides:','}){").append(this.lineSeparator);
            valSql.append(this.lineSeparator).append(this.getStatementStart());
            valSql.append("trim({suffixOverrides:','}){").append(this.lineSeparator);
        }
        IdType idType = IdType.AUTO;
        SQLTableSource source = new SQLTableSource();
        Iterator<String> cols = classDesc.getInCols().iterator();
        Iterator<String> attrs = classDesc.getAttrs().iterator();

        List<String> idCols = classDesc.getIdCols();
        while (cols.hasNext() && attrs.hasNext()) {
            String col = cols.next();
            String attr = attrs.next();
            if (classDesc.isInsertIgnore(attr)) {
                continue;
            }

            if (idCols.size() == 1 && idCols.contains(col)) {

                idType = this.getIdType(classDesc.getTargetClass(), attr);
                if (idType == IdType.AUTO) {
                    continue; //忽略这个字段
                } else if (idType == IdType.SEQUENCE) {

                    colSql.append(appendInsertColumn(cls, table, col));
                    Id seqId = BeanKit.getAnnotation(classDesc.getTargetClass(), attr,
                            (Method) classDesc.getIdMethods().get(attr), Id.class);

                    valSql.append(this.getSequenceValue(seqId.name()) + COMMA);
                    continue;
                } else if (idType == IdType.ASSIGN) {
                    //normal
                }
            }

            if (attr.equals(classDesc.getVersionProperty()) && classDesc.getInitVersionValue() != -1) {
                //版本字段
                colSql.append(appendInsertColumn(cls, table, col));
                valSql.append(classDesc.getInitVersionValue()).append(COMMA);
                continue;
            }
            if (template) {
                colSql.append(appendInsertTemplateColumn(cls, table, attr, col));
                valSql.append(appendInsertTemplateValue(cls, table, attr, col));
            } else {
                colSql.append(appendInsertColumn(cls, table, col));
                valSql.append(appendInsertValue(cls, table, attr, col));
            }

        }


        if (template) {
            //结束trim(){}
            colSql.append(this.lineSeparator).append(this.getStatementStart());
            colSql.append("}").append(this.lineSeparator).append(this.getStatementEnd());
            colSql.append(")");
            valSql.append(this.lineSeparator).append(this.getStatementStart());
            valSql.append("}").append(this.lineSeparator).append(this.getStatementEnd());
            valSql.append(")");
            sql.append(colSql).append(valSql);
        } else {
            sql.append(removeComma(colSql, null).append(")").append(removeComma(valSql, null)).append(")").toString());

        }
        source.setTemplate(sql.toString());
        source.setIdType(idType);
        source.setTableDesc(table);
        if (idType == IdType.ASSIGN) {
            Map<String, Id> map = new HashMap<>();
            for (String idAttr : classDesc.getIdAttrs()) {
                Method getter = (Method) classDesc.getIdMethods().get(idAttr);
                Id assignId = BeanKit.getAnnotation(classDesc.getTargetClass(), idAttr, getter, Id.class);
                if (assignId != null && assignId.name().length() != 0) {

                    map.put(idAttr, assignId);
                }
            }

            if (map.size() != 0) {
                source.setIds(map);
            }

        }

        return source;
    }

    /**
     * 子类可以实现自己的特定定insert语句，比如insert ignore into，等
     *
     * @param classDesc
     * @param table
     * @return
     */
    protected String getInsertBaseSql(ClassDesc classDesc, TableDesc table) {
        return "insert into " + getTableName(table);
    }

    /****
     * 根据table生成字段名列表
     *
     * @param table
     * @return
     */
    @Override
    public String genColumnList(String table) {
        Set<String> colSet = getCols(table);
        if (null == colSet || colSet.isEmpty()) {
            return EMPTY;
        }
        StringBuilder cols = new StringBuilder();
        for (String col : colSet) {
            cols.append(col).append(COMMA);
        }
        return cols.deleteCharAt(cols.length() - 1).toString();
    }

    /***
     * 获取字段集合
     *
     * @param tableName
     * @return
     */
    @Override
    public Set<String> getCols(String tableName) {

        TableDesc table = this.metadataManager.getTable(tableName);
        return table.getCols();

    }

    /***
     * 生成通用条件语句 含有Empty判断
     *
     * @param tableName
     * @return
     */
    @Override
    public String genCondition(String tableName) {
        TableDesc table = this.metadataManager.getTable(tableName);
        ClassDesc classDesc = table.getClassDesc(nameConversion);
        Set<String> attrSet = classDesc.getAttrs();
        if (null == attrSet || attrSet.isEmpty()) {
            return EMPTY;
        }

        Iterator<String> attrIt = attrSet.iterator();
        Iterator<String> colIt = table.getCols().iterator();
        StringBuilder condition = new StringBuilder();
        Set<String> colsIds = table.getIdNames();
        while (colIt.hasNext() && attrIt.hasNext()) {
            String col = colIt.next();
            String attr = attrIt.next();
            if (colsIds.contains(col)) {
                continue;
            }
            condition.append(appendWhere(null, table, col, attr));
        }
        return "1 = 1  \n" + condition.toString();
    }

    /***
     * 生成通用的col=property (示例：age=${age},name=${name}) 含有Empty判断
     *
     * @param tableName
     * @return
     */
    @Override
    public String genColAssignProperty(String tableName) {
        TableDesc table = this.metadataManager.getTable(tableName);
        ClassDesc classDesc = table.getClassDesc(nameConversion);

        Iterator<String> cols = classDesc.getInCols().iterator();
        Iterator<String> properties = classDesc.getAttrs().iterator();

        StringBuilder sql = new StringBuilder();
        while (cols.hasNext() && properties.hasNext()) {
            String col = cols.next();
            String prop = properties.next();
            sql.append(appendSetColumn(null, table, col, prop));
        }

        return sql.deleteCharAt(sql.length() - 1).toString();
    }

    /***
     * 生成通用的col=property (示例：age=${age},name=${name}) 没有Empty判断
     *
     * @param tableName
     * @return
     */
    @Override
    public String genColAssignPropertyAbsolute(String tableName) {
        TableDesc table = this.metadataManager.getTable(tableName);
        ClassDesc classDesc = table.getClassDesc(nameConversion);
        Set<String> colSet = classDesc.getInCols();
        Set<String> properties = classDesc.getAttrs();
        if (null == colSet || colSet.isEmpty()) {
            return "";
        }
        StringBuilder sql = new StringBuilder();
        Iterator<String> colIt = colSet.iterator();
        Iterator<String> propertiesIt = properties.iterator();

        while (colIt.hasNext() && propertiesIt.hasNext()) {
            String col = colIt.next();
            String prop = propertiesIt.next();
            sql.append(appendSetColumnAbsolute(null, table, col, prop));
        }
        return sql.deleteCharAt(sql.length() - 1).toString();
    }


    /****
     * 去掉逗号后面的加上结束符和条件并换行
     *
     * @param sql
     * @return
     */
    protected StringBuilder removeComma(StringBuilder sql, String condition) {
        int index = sql.lastIndexOf(COMMA);
        if (index == -1) {
            //这种情况发生在没有列名，通常是 insert xxx () values()
            return sql;
        } else {
            return sql.deleteCharAt(index).append((condition == null ? EMPTY : condition));
        }

    }

    /***
     * 生成一个追加在set子句的后面sql(示例：name=${name},)
     *
     * @param c
     * @param table
     * @param fieldName
     * @return
     */
    protected String appendSetColumnAbsolute(Class<?> c, TableDesc table, String colName, String fieldName) {
        String sql = EMPTY;
        ClassDesc classDesc = table.getClassDesc(c, this.nameConversion);
        AttributeBuilderHolder handler = (AttributeBuilderHolder) classDesc.getColHandlers().get(fieldName);
        if (handler != null && handler.supportPersistGen()) {
            AttributePersistBuilder baseAttributeBuilder = (AttributePersistBuilder) handler.getInstance();
            sql = baseAttributeBuilder.toSql(this, fieldName, colName, handler.getAnnotation(), table);

        }
        return this.getKeyWordHandler().getCol(colName) + "=" + this.getHolderStart() + sql + this.getHolderEnd() + ",";
    }

    /***
     * 生成一个追加在set子句的后面sql(示例：name=${name},)有Empty判断
     *
     * @param c
     * @param table
     * @param fieldName
     * @return
     */
    protected String appendSetColumn(Class<?> c, TableDesc table, String colName, String fieldName) {
        String sql = EMPTY;
        ClassDesc classDesc = table.getClassDesc(c, this.nameConversion);
        AttributeBuilderHolder handler = (AttributeBuilderHolder) classDesc.getColHandlers().get(fieldName);
        if (handler != null && handler.supportPersistGen()) {
            // 注解忽略模板相关功能
            AttributePersistBuilder baseAttributeBuilder = (AttributePersistBuilder) handler.getInstance();
//            return baseAttributeBuilder.toSql(this,fieldName,colName,handler.getBeanAnnotatoin(),table);
            sql = baseAttributeBuilder.toSql(this, fieldName, colName, handler.getAnnotation(), table);
        }

        return this.getStatementStart() + "if(!isEmpty(" + sql + ")){"
                + this.getStatementEnd() + "\t" + this.getKeyWordHandler().getCol(colName) + "=" + this.getHolderStart() + sql + this.getHolderEnd() + ","
                + lineSeparator + this.getStatementStart() + "}" + getStatementEnd();


    }


    /*****
     * 生成一个追加在where子句的后面sql(示例：and name=${name} )
     *
     * @param c
     * @param table
     * @param fieldName
     * @return
     */
    protected String appendWhere(Class<?> c, TableDesc table, String colName, String fieldName) {
        String prefix = EMPTY;

        String connector = " and ";
        return this.getStatementStart() + "if(!isEmpty(" + prefix + fieldName + ")){"
                + this.getStatementEnd() + connector + this.getKeyWordHandler().getCol(colName) + "=" + this.getHolderStart() + prefix + fieldName
                + this.getHolderEnd() + lineSeparator + this.getStatementStart() + "}" + this.getStatementEnd();

    }


    /****
     * 生成一个追加在insert into 子句的后面sql(示例：name,)
     *
     * @param c
     * @param table
     * @param colName
     * @return
     */
    protected String appendInsertColumn(Class<?> c, TableDesc table, String colName) {
        return this.getKeyWordHandler().getCol(colName) + COMMA;
    }

    /****
     * 生成一个追加在insert into value子句的后面sql(示例：name=${name},)
     *
     * @param table
     * @param fieldName
     * @return
     */
    protected String appendInsertValue(Class<?> c, TableDesc table, String fieldName, String colName) {
        String sql = fieldName;
        ClassDesc classDesc = table.getClassDesc(c, this.nameConversion);
        AttributeBuilderHolder handler = (AttributeBuilderHolder) classDesc.getColHandlers().get(fieldName);
        if (handler != null && handler.supportPersistGen()) {
            AttributePersistBuilder baseAttributeBuilder = (AttributePersistBuilder) handler.getInstance();
            sql = baseAttributeBuilder.toSql(this, fieldName, colName, handler.getAnnotation(), table);

        }


        return this.getHolderStart() + sql + this.getHolderEnd() + COMMA;

    }

    /****
     * 生成一个追加在insert into 子句的后面sql(示例：name,)
     * 需要判断值是否空，如果为空，则不作插入
     * @param c
     * @param table
     * @param fieldName
     * @param colName
     * @return
     */
    protected String appendInsertTemplateColumn(Class<?> c, TableDesc table, String fieldName, String colName) {
        String col = this.getKeyWordHandler().getCol(colName);
        String sql = fieldName;
        ClassDesc classDesc = table.getClassDesc(c, this.nameConversion);
        AttributeBuilderHolder handler = (AttributeBuilderHolder) classDesc.getColHandlers().get(fieldName);

        if (handler != null && handler.supportPersistGen()) {
            AttributePersistBuilder baseAttributeBuilder = (AttributePersistBuilder) handler.getInstance();
            sql = baseAttributeBuilder.toSql(this, fieldName, colName, handler.getAnnotation(), table);
        }

        if (col.startsWith("'")) {
            return this.getHolderStart() + "db.testColNull(" + sql + ",\"" + col + "\")" + this.getHolderEnd();
        } else {
            return this.getHolderStart() + "db.testColNull(" + sql + ",'" + col + "')" + this.getHolderEnd();
        }

    }


    /****
     * 生成一个追加在insert into value子句的后面sql(示例：name=${name},)
     *需要判断值是否空，如果为空，则不作插入
     * @param table
     * @param fieldName
     * @return
     */
    protected String appendInsertTemplateValue(Class<?> c, TableDesc table, String fieldName, String colName) {
        String sql = fieldName;

        ClassDesc classDesc = table.getClassDesc(c, this.nameConversion);
        AttributeBuilderHolder handler = (AttributeBuilderHolder) classDesc.getColHandlers().get(fieldName);
        if (handler != null && handler.supportPersistGen()) {
            AttributePersistBuilder baseAttributeBuilder = (AttributePersistBuilder) handler.getInstance();
            sql = baseAttributeBuilder.toSql(this, fieldName, colName, handler.getAnnotation(), table);
        }
        return this.getHolderStart() + "db.testNull(" + sql + "!,\"" + fieldName + "\")" + this.getHolderEnd();

    }

    /***
     * 生成主键条件子句（示例 whrer 1=1 and id=${id}）
     *
     * @param cls
     * @return
     */
    protected String appendIdCondition(Class<?> cls) {
        String tableName = nameConversion.getTableName(cls);
        StringBuilder condition = new StringBuilder(" where ");
        TableDesc table = metadataManager.getTable(tableName);
        ClassDesc classDesc = table.getClassDesc(cls, nameConversion);

        List<String> colIds = classDesc.getIdCols();
        List<String> propertiesIds = classDesc.getIdAttrs();
        this.checkId(colIds, propertiesIds, cls.getName());
        Iterator<String> colIt = colIds.iterator();
        Iterator<String> propertiesIt = propertiesIds.iterator();
        if (colIt.hasNext() && propertiesIt.hasNext()) {
            String colId = colIt.next();
            String properId = propertiesIt.next();
            condition.append(this.getKeyWordHandler().getCol(colId)).append(" = ")
                    .append(this.getHolderStart()).append(properId).append(this.getHolderEnd());
            while (colIt.hasNext() && propertiesIt.hasNext()) {
                colId = colIt.next();
                properId = propertiesIt.next();
                condition.append(" and ").append(this.getKeyWordHandler().getCol(colId)).append(" = ")
                        .append(this.getHolderStart()).append(properId).append(this.getHolderEnd());
            }
        }

        return condition.toString();
    }


    protected String genDateAnnotationSql(DateTemplate t, Class c, String col) {
        String accept = t.accept();
        String[] vars = null;
        if (accept == null || accept.length() == 0) {
            String col1 = col.substring(0, 1).toUpperCase() + col.substring(1);
            vars = new String[]{DateTemplate.MIN_PREFIX + col1, DateTemplate.MAX_PREFIX + col1};
        } else {
            vars = t.accept().split(COMMA);
        }


        String[] comp = null;
        String compare = t.compare();
        if (compare == null || compare.length() == 0) {
            comp = new String[]{DateTemplate.LARGE_OPT, DateTemplate.LESS_OPT};

        } else {
            comp = t.compare().split(COMMA);
        }

        String prefix = EMPTY;

        String connector = " and ";
        String sql = this.getStatementStart() + "if(!isEmpty(" + prefix + vars[0] + ")){"
                + this.getStatementEnd() + connector + col + comp[0] + this.getHolderStart() + vars[0] + this.getHolderEnd() + lineSeparator + this.getStatementStart() + "}" + this.getStatementEnd();

        sql = sql + this.getStatementStart() + "if(!isEmpty(" + prefix + vars[1] + ")){"
                + this.getStatementEnd() + connector + col + comp[1] + this.getHolderStart() + vars[1] + this.getHolderEnd() + lineSeparator + this.getStatementStart() + "}" + this.getStatementEnd();
        return sql;

    }

    protected String getTableName(TableDesc desc) {
        String tableName = desc.getName();
        int index = -1;
        if ((index = tableName.indexOf(this.getStatementStart())) != -1) {
            //表名字包含了特殊符号，比如Oracle 的@
            tableName = tableName.substring(0, index) + "\\" + tableName.substring(index);
        }
        if (desc.getSchema() != null) {
            return this.getKeyWordHandler().getTable(desc.getSchema()) + DOT + this.getKeyWordHandler().getTable(tableName);
        } else {
            return this.getKeyWordHandler().getTable(tableName);
        }

    }

    protected void checkId(Collection colsId, Collection attrsId, String clsName) {
        if (colsId.size() == 0 || attrsId.size() == 0) {
            throw new OrmCoreException(ID_NOT_FOUND, clsName);
        }
    }

    protected String getOrderBy() {
        return lineSeparator + this.getHolderStart() + "text(has(_orderBy)?' order by '+_orderBy)" + this.getHolderEnd() + " ";
    }

    /* 根据注解来决定主键采用哪种方式生成。在跨数据库应用中，可以为一个id指定多个注解方式，如mysql，postgres 用auto，oracle 用seq
     */
    @Override
    public IdType getIdType(Class c, String idProperty) {
        List<Annotation> ans = BeanKit.getAllAnnotation(c, idProperty);
        IdType idType = null;
        for (Annotation an : ans) {
            if (an instanceof Id) {
                Id id = (Id) an;
                idType = id.strategy();
            }
        }

        if (idType == null) {
            idType = IdType.AUTO;
        }

        return idType;

    }


    /***
     * 获取SQLSource
     * @param sqlSourceType SqlSourceType
     * @param  clazz Class
     * @return SQLSource SQLSource
     */
    @Override
    public SQLSource getSqlSource(SqlSourceType sqlSourceType,Class<?> clazz) {
        SQLSource sqlSource=null;
        switch (sqlSourceType) {
            case SELECT_BY_ID: {
                sqlSource = this.genSelectById(clazz);
                break;
            }
            case EXIST_BY_ID:{
                sqlSource = this.genExistSql(clazz);
                break;
            }
            case SELECT_BY_TEMPLATE: {
                sqlSource = this.genSelectByTemplate(clazz);
                break;
            }
            case SELECT_COUNT_BY_TEMPLATE: {
                sqlSource = this.genSelectCountByTemplate(clazz);
                break;
            }
            case DELETE_BY_ID: {
                sqlSource = this.genDeleteById(clazz);
                break;
            }
            case SELECT_ALL: {
                sqlSource = this.genSelectAll(clazz);
                break;
            }
            case UPDATE_ALL: {
                sqlSource = this.genUpdateAll(clazz);
                break;
            }
            case UPDATE_BY_ID: {
                sqlSource = this.genUpdateById(clazz);
                break;
            }

            case UPDATE_TEMPLATE_BY_ID: {
                sqlSource = this.genUpdateTemplate(clazz);
                break;
            }

            case INSERT: {
                sqlSource = this.genInsert(clazz);
                break;
            }

            case INSERT_TEMPLATE: {
                sqlSource = this.genInsertTemplate(clazz);
                break;
            }
            case DELETE_TEMPLATE_BY_ID:
                sqlSource = this.genDeleteById(clazz);
                break;
            case LOCK_BY_ID: {
                sqlSource = this.genSelectByIdForUpdate(clazz);
                break;
            }
            default: {
                throw new UnsupportedOperationException();
            }
        }
        return sqlSource;
    }
}